package cn.flying.cloud.base.common.utils.crypto;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 国密SM4分组密码算法工具类（对称加密）,用于替代DES/AES等国际算法
 * SM4为无线局域网标准的分组加密算法，对称加密，用于替代DES/AES等国际算法，于2012年3月21日发布，
 * SM4算法与 AES算法具有相同的密钥长度和分组长度，均为128位，故对消息进行加解密时，若消息长度过长，需要进行分组，要消息长度不足，则要进行填充。
 * 加密算法与密钥扩展算法都采用32轮非线性迭代结构，解密算法与加密算法的结构相同，只是轮密钥的使用顺序相反，解密轮密钥是加密轮密钥的逆序
 *
 * @author: admin
 * @date: 2025年01月21日 21:40
 * @version: 1.0
 */
public class SM4Util {
    private static final Logger logger = LoggerFactory.getLogger(SM4Util.class);

    private static final String ALGORITHM = "SM4";
    private static final String ALGORITHM_ECB_PKCS5PADDING = "SM4/ECB/PKCS5Padding"; // 模式：ECB 填充：

    static {
        // 防止内存中出现多次BouncyCastleProvider的实例
        if (null == Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    /**
     * 生成密钥文本
     *
     * @param keySeed — 用于生成密钥文本的随机值（种子，盐值），值一致，生成的密钥文本就一致。
     * @return :密钥文本，用于后续进行加解密
     */
    public static String generateKey(String keySeed) {
        try {
            KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
            SecureRandom random = new SecureRandom(keySeed.getBytes(StandardCharsets.UTF_8));
            keyGen.init(random);
            SecretKey secretKey = keyGen.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (Exception e) {
            logger.error("密钥生成失败", e);
            return "";
        }
    }

    /**
     * Sm4 加密
     *
     * @param data   被加密的数据
     * @param secret 密钥文本
     * @return 加密好的密文(base64)
     * @throws Exception
     */
    public static String encrypt(String data, String secret) throws Exception {
        SecretKey secretKey = secretKey(secret);
        byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
        // 加密
        Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME);
        // 添加向量
//        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
//        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] encryptBytes = cipher.doFinal(dataBytes);
        return Base64.getEncoder().encodeToString(encryptBytes);
    }

    /**
     * 解密
     *
     * @param cipherText 密文(base64)
     * @param secret     :密钥文本
     * @return 明文
     */
    public static String decrypt(String cipherText, String secret) throws Exception {
        SecretKey secretKey = secretKey(secret);
        byte[] cipherTextBytes = Base64.getDecoder().decode(cipherText);
        // 加密
        Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME);
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] decryptBytes = cipher.doFinal(cipherTextBytes);
        return new String(decryptBytes, StandardCharsets.UTF_8);
    }

    private static SecretKey secretKey(String key) {
        byte[] bytes = Base64.getDecoder().decode(key);
        return new SecretKeySpec(bytes, ALGORITHM);
    }

//    public static void main(String[] args) throws Exception {
//        String data = "国密算法：即我国自主研制的算法，摆脱对国外的技术过度依赖，国密即国家密码局认定的国产密码算法。以下是具体的对应关系：对称算法：AES、DES、SM4；非对称算法：RSA、SM2；摘要/杂凑算法：MD5、SHA-I、SM3";
//        String keySrc = UUID.randomUUID().toString();
//
//        System.out.println("keySrc:" + keySrc);
//        String secret = Sm4Util.generateKey(keySrc);
//        // 密钥:/064css82SOrw1HfCCoAJQ==
//        System.out.println("密钥:" + secret);
//
//        String cipherText = Sm4Util.encrypt(data, secret);
//        System.out.println("密文:" + cipherText);
//
//        String deStr = Sm4Util.decrypt(cipherText, secret);
//        System.out.println("明文:" + deStr);
//    }

}
